;
;  PROGRAM:  PHELP
;  AUTHOR:  Richard Conn
;  VERSION:  2.0
;  DATE:  28 Apr 83
;  PREVIOUS VERSIONS:  None
;  DERIVATION:  PHELP 1.2 (12 July 80)
;
VERS	equ	20

FALSE	EQU	0
TRUE	EQU	NOT FALSE

timeok	equ	FALSE	;TRUE if TIME enabled, FALSE if not

;
;	PHELP is used to print out a HLP file.  It breaks pages on each
; information section and ignores form feeds, so the data is presented
; in a sequential fashion.  It is very convenient to provide online
; documentation in the form of HLP files, and this utility allows the user
; to readily print out this documentation.  PHELP is preferred over PRINT3
; for printing HLP files because of its knowledge of their structure and
; its special way of handling them.
;
;	PHELP is invoked by a command line of the following form:
;		PHELP file1,file2,...,filen o...
; where each "filen" is an ambiguous file name and "o" is zero or more of
; the following options:
;	H@head@	Heading Text
;			The user may specify the text of the heading to
;			appear at the top of every page
;	I	Inspect Files
;			The user approves each file to be printed
;			before the printing process begins
;	L	Toggle Line Numbering
;			Each line may or may not begin with a line number
;	Snnnn	Skip to Specified Page
;			Printing begins at the indicated page
;	T	Toggle Time Display
;			Time/Date information is optionally included
;			in the page header
;

;
;	This program is Copyright (c) 1983 by Richard Conn
;	All Rights Reserved
;
;	ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;	The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;

;
;  BASIC SYSLIB ROUTINES NEEDED BY TEMPLATE
;
C$ESIZE	EQU	16	; SIZE OF DIR ENTRY (FROM SYSLIB DIRF ROUTINE)

	EXT	DIRFS	; DIRECTORY PROCESSOR
	EXT	DIRPACK	; PACK DIRECTORY

	EXT	ZGPINS	; INIT BUFFERS
	EXT	ZFNAME	; FILE NAME PROCESSOR

	EXT	INITFCB	; INIT FCB
	EXT	RETUD	; RETURN CURRENT USER/DISK
	EXT	PUTUD	; SAVE CURRENT USER/DISK
	EXT	GETUD	; RESTORE CURRENT USER/DISK
	EXT	LOGUD	; LOG INTO USER/DISK
	EXT	PRINT	; PRINT STRING PTED TO BY RET ADR
	EXT	PADC	; PRINT A AS DECIMAL CHARS
	EXT	COUT	; CONSOLE OUTPUT ROUTINE
	EXT	CST	; CONSOLE STATUS ROUTINE
	EXT	CIN	; CONSOLE INPUT ROUTINE
	EXT	CAPS	; CAPITALIZE ROUTINE
	EXT	CRLF	; NEW LINE ROUTINE
	EXT	CLINE	; COMMAND LINE STRING SAVE ROUTINE
	EXT	CODEND	; CODE END COMPUTATION ROUTINE

	EXT	F$OPEN	; FILE OPEN
	EXT	F$READ	; BLOCK READ
	EXT	F$CLOSE	; FILE CLOSE

	ext	eval10
	ext	lcrlf
	ext	lpstr
	ext	lprint
	ext	lout
	ext	lhldc
	ext	condin
	ext	moveb
;
	if	timeok
	ext	time
	endif
;

;
;  Insert Function-Required Library References Here
;

;
;  CP/M EQUATES
;
CPM	EQU	0	; WARM BOOT
BDOSE	EQU	CPM+5	; BDOS ENTRY
FCB	EQU	CPM+5CH	; FCB
TBUFF	EQU	CPM+80H	; INPUT LINE BUFFER
DEL	EQU	7FH	; <DEL>
CR	EQU	13	; <CR>
FF	EQU	12	; <FF>
LF	EQU	10	; <LF>
CTRLC	EQU	'C'-'@'	; ^C
CTRLG	EQU	'G'-'@'
CTRLH	EQU	'H'-'@'
CTRLI	EQU	'I'-'@'
CTRLS	EQU	'S'-'@'
CTRLX	EQU	'X'-'@'
CTRLZ	EQU	'Z'-'@'
eold	equ	0FFH	;End of Load Indicator

;
;  Branch to Start of Program
;
	JMP	T$START

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;	This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
	DB	0FFH	; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
	DW	40H	; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
	DB	0,0	; DISK, USER FOR FIRST PATH ELEMENT
			; DISK = 1 FOR A, '$' FOR CURRENT
			; USER = NUMBER, '$' FOR CURRENT
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0	; DISK, USER FOR 8TH PATH ELEMENT
	DB	0	; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
	DB	0FFH	; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
	DW	0FF00H	; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
	DB	4	; MAXIMUM NUMBER OF DISKS
MUSER:
	DB	31	; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
	DB	0FFH	; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
	DB	0FFH	; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
	DB	10	; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
	DB	'chdir',0	; PASSWORD FOR MOVING INTO PRIV USER AREAS
	DS	41-($-PPASS)	; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
	DB	'$'	; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
	DW	80H	; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
	DW	00000H	; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
	DB	64	; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
	DB	'NAMES   '	; NAME OF DISK NAME FILE
	DB	'DIR'		; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
	DB	0FFH	; EXTERNAL PATH?
MCREQD:
	DB	000H	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	0FFH	; MAX USER/DISK?
UDREQD:
	DB	000H	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	0FFH	; CURRENT INDIC AND DMA?
NDREQD:
	DB	0FFH	; NAMED DIRECTORIES?
Z2CLASS:
	DB	11	; CLASS 11
	DB	'ZCPR2'
	DS	10	; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  **** Special Initial Value Area for GENINS
;
LWIDTH:
	DB	132	; PRINTER LINE WIDTH
LTPP:
	DB	44	; LINES OF TEXT PER PAGE
LSPP:
	DB	5	; LINES TO SKIP PER PAGE
CWIDTH:
	DB	80	; WIDTH OF SCREEN
CTPP:
	DB	22	; LINES OF TEXT PER SCREEN
CSPP:
	DB	1	; LINES TO SKIP PER SCREEN
;
;  NOTE:  LTPP + LSPP + 2 (HEADER SIZE) = TOTAL LINES PER PAGE ON PRINTER
;
DLNUMFL:
	DB	0	; LINE NUMBER FLAG (DEFAULT TO NO)
DTIMEPFL:
	DB	0FFH	; TIME PRINT FLAG (DEFAULT TO YES)
DINSPECT:
	DB	0	; INSPECT FILES (DEFAULT TO NO)
;
;  WORKING BUFFERS
;
LNUMFL:
	DB	0	; LINE NUMBER FLAG
TIMEPFL:
	DB	0FFH	; TIME PRINT FLAG
INSPECT:
	DB	0	; INSPECT FLAG
SKIPFL:
	DB	0	; SKIP FLAG (DEFAULT TO NO)
SKIPNUM:
	DS	2	; PAGE NUMBER TO SKIP TO
LNUM:
	DS	2	; CURRENT LINE NUMBER
PNUM:
	DS	2	; CURRENT PAGE NUMBER
HBUF:
	DS	100	; BUFFER FOR HEADING
;
	if	timeok
TIMEBF:
	DS	100	; BUFFER FOR TIME STAMP
	endif
;
;  Start of Program
;
T$START:
	LXI	H,0	; GET STACK PTR
	DAD	SP
	SHLD	V$STACK	; SAVE IT
	LXI	SP,V$STACK	; SET SP
	XRA	A	; A=0
	STA	V$INSPECT	; TURN OFF FILE INSPECTION
	CALL	PUTUD	; SAVE CURRENT USER/DISK AWAY
	CALL	RETUD	; GET CURRENT USER/DISK
	MOV	A,B	; SAVE DISK
	STA	V$DISK
	MOV	A,C	; SAVE USER
	STA	V$USER

	CALL	ZGPINS	; INIT BUFFERS

	LXI	H,TBUFF	; SAVE COMMAND LINE
	CALL	CLINE
	SHLD	V$CMDLNE	; SAVE PTR

;
;  Banner of Program
;
	CALL	BANNER	; PRINT BANNER PROVIDED BY USER
;
;  Check for Help Request
;
	LDA	FCB+1	; GET FIRST CHAR OF FILE NAME
	CPI	' '	; NO FILE SPEC?
	JZ	T$HELPENT
	CPI	'/'	; OPTION CAUGHT?
	JNZ	T$ECONT

;
;  Print Help Information
;
T$HELPENT:
	CALL	HELP	; PRINT PROGRAM'S HELP MESSAGE

;
;  RETURN TO OS
;
T$RETURN:
	CALL	DINIT	; DEINIT PROGRAM
	LHLD	V$STACK	; GET OLD STACK
	SPHL		; SET IT
	RET

;
;  PROGRAM'S INIT ROUTINE
;
T$ECONT:
	CALL	INIT	; PROG INIT ROUTINE
;
;  EXTRACT FLAGS IF PRESENT
;
	LXI	H,0	; SET FILE COUNT
	SHLD	V$FILECNT
	LHLD	V$CMDLNE	; PT TO BUFFER
;
;  SKIP TO FILE NAME STRING
;
	CALL	T$SBLANK	; SKIP OVER BLANKS
;
;  SKIP TO END OF FILE NAME STRING
;
	CALL	T$SNBLANK	; SKIP OVER NON-BLANKS
;
;  CHECK FOR LEADING SLASH ON OPTION AND SKIP IT IF SO
;
T$OPT:
	CPI	'/'	; OPTION CHAR?
	JNZ	T$OPTION
	INX	H	; SKIP SLASH
;
;  PROCESS LIST OF OPTIONS
;
T$OPTION:
	MOV	A,M	; GET BYTE
	ORA	A	; DONE?
	JZ	T$DSPEC
	INX	H	; PT TO NEXT CHAR
	CPI	' '	; SKIP OVER SPACES
	JZ	T$OPTION
	MOV	C,A	; COMMAND IN C
	LXI	D,OPTAB	; PT TO OPTION TABLE
T$OPTL:
	LDAX	D	; GET OPTION LETTER
	ORA	A	; END OF TABLE?
	JZ	T$HELPENT	; HELP IF SO
	CMP	C	; MATCH?
	JZ	T$OPTM	; PROCESS IF SO
	INX	D	; PT TO NEXT ENTRY
	INX	D
	INX	D
	JMP	T$OPTL
;
;  PROCESS OPTION
;
T$OPTM:
	PUSH	H	; SAVE HL ON STACK
	LXI	H,T$OPTION	; GET RETURN ADDRESS
	XTHL		; ... ON STACK AND RESTORE HL
	INX	D	; PT TO ADDRESS
	LDAX	D	; GET ADDRESS LOW
	MOV	B,A	; ... IN B
	INX	D
	LDAX	D	; GET ADDRESS HIGH
	MOV	D,A	; ... IN D
	MOV	E,B	; LOW IN E
	PUSH	D	; PUT ADDRESS ON STACK
	MOV	A,C	; COMMAND IN A
	RET		; "CALL" OPTION ROUTINE
;
;  BEGIN MOVING THROUGH FILE NAMES, SEPARATED BY COMMAS
;
T$DSPEC:
	LHLD	V$CMDLNE	; PT TO FIRST BYTE
	CALL	T$SBLANK	; SKIP TO NON-BLANK
;
;  MAJOR REENTRY POINT WHEN FILE SPECS ARE SEPARATED BY COMMAS
;    HL PTS TO FIRST BYTE OF NEXT FILE SPEC
;
T$DSPEC1:
	LXI	SP,V$STACK	; RESET STACK
	CALL	GETUD	; RESET USER IF NECESSARY
	LXI	D,V$NTFCB	; PT TO FCB IN DE, PT TO 1ST CHAR OF FN IN HL

	CALL	ZFNAME	; EXTRACT FILE NAME INTO FCB, AND GET DISK AND USER
	JZ	T$DERR	; ERROR HANDLER

	push	b	;save disk/user
	push	h	;save others
	push	d
	lxi	h,hlptyp	;optionally set default HLP type
	lxi	d,V$NTFCB+9	;check for any specified
	ldax	d	;' ' means none
	cpi	' '
	jnz	hlpskp
	mvi	b,3	;3 chars
	call	moveb
hlpskp:
	pop	d	;restore others
	pop	h
	pop	b	;restore disk/user
	SHLD	V$NEXTCH	; SAVE PTR TO DELIMITER WHICH ENDED SCAN
	MOV	A,B	; SAVE POSSIBLE DRIVE SPEC
	CPI	0FFH	; CURRENT DISK?
	JZ	T$DSPEC2
	LDA	MDISK	; GET MAX DISK NUMBER
	DCR	B	; ADJUST TO WITHIN BOUNDS 0-15
	CMP	B	; WITHIN BOUNDS?
	MOV	A,B	; GET DISK NUMBER IN A
	JNC	T$USPEC
T$DERR:
	CALL	PRINT
	DB	CR,LF,'Invalid Disk Specification',0
	JMP	T$RETURN

;
;  SET CURRENT DISK
;
T$DSPEC2:
	LDA	V$DISK	;GET CURRENT DISK
	MOV	B,A	;... IN B

;  CHECK FOR USER NUMBER
T$USPEC:
	MOV	A,C	; GET NEW USER NUMBER
	CPI	0FFH	; DEFAULT USER?
	JZ	T$USPEC1
	CPI	'?'	; ALL USERS NOT ALLOWED?
	JZ	T$UERR
	LDA	MUSER	; GET MAX USER NUMBER
	CMP	C
	MOV	A,C	; USER NUMBER IN A
	JNC	T$FCT
T$UERR:
	CALL	PRINT
	DB	CR,LF,'Invalid User Number',0
	JMP	T$RETURN
T$USPEC1:
	LDA	V$USER	;GET CURRENT USER
	MOV	C,A	;... IN C

;
;  LOAD DIRECTORY AND PERFORM FUNCTION
;
T$FCT:
	MOV	A,B	; SAVE NEW DISK/USER AWAY
	STA	V$CDISK	; CURRENT DISK
	MOV	A,C
	STA	V$CUSER	; CURRENT USER
	CALL	LOGUD	; LOG INTO ACCOUNT
	CALL	CODEND	; PT TO END OF CODE
	CALL	RETUD	; GET USER NUMBER FOR DIRFS
	MVI	A,11000000B	; SELECT SYS AND NON-SYS FILES
	ORA	C	; OR IN USER NUMBER
	LXI	D,V$NTFCB	; PT TO FCB
	CALL	INITFCB	; INIT THE FCB
	CALL	DIRFS	; LOAD DIR, SELECT FILES, PACK, AND ALPHABETIZE
;
;  DETERMINE BEGINNING OF SCRATCH AREA (SCRATCH) AND SIZE IN PAGES (BCNT)
;
	PUSH	H	; SAVE PTR AND COUNT
	PUSH	B
	LXI	D,C$ESIZE	; SET PTR TO NEXT FREE BLOCK
T$FCTFRE:
	MOV	A,B	; DONE?
	ORA	C
	JZ	T$FCTFR1
	DAD	D	; PT TO NEXT
	DCX	B	; COUNT DOWN
	JMP	T$FCTFRE
T$FCTFR1:
	INR	H	; NEXT PAGE
	MVI	L,0
	SHLD	V$SCRATCH	; SET PTR TO SCRATCH AREA
	XCHG		; PTR IN DE
	LHLD	BDOSE+1	; COMPUTE BLOCK BUFFER SIZE
	MOV	A,H	; ADJUST FOR ZCPR2
	SUI	10
	SUB	D	; A=SIZE IN BLOCKS
	STA	V$BCNT	; SET BLOCK COUNT
	POP	B	; RESTORE AND SAVE REGS
	POP	H
;
;  ALLOW USER TO INSPECT FILES
;
	PUSH	H
	PUSH	B
	CALL	T$ICHECK	; CHECK FOR INSPECT OPTION AND INSPECT IF SET
	POP	B	; RESTORE COUNT AND PTR
	POP	H

;
;  PERFORM FUNCTION; HL PTS TO FILE AND BC CONTAINS NUMBER OF FILES
;
T$FCTL:
	MOV	A,B		; CHECK FOR COMPLETION (COUNT = 0)
	ORA	C
	JZ	T$FCTL1
	DCX	B		; COUNT DOWN
	LXI	SP,V$STACK	; RESET STACK
	PUSH	B		; SAVE COUNT AND PTR
	PUSH	H
	MOV	A,M		; SELECTED FILE?
	ORA	A		; 0=YES
	CZ	FUNCTION	; PERFORM FUNCTION
;
;  ENTRY POINT TO SKIP TO NEXT FILE IN LIST
;
E$FCTLNXT:
	LXI	SP,V$STACK-4	; RESTORE STACK
	POP	H		; RESTORE PTR
	POP	B		; RESTORE COUNT
	LXI	D,C$ESIZE	; PT TO NEXT ENTRY
	DAD	D
	JMP	T$FCTL

;
;  CHECK FOR NEXT FILE SPEC
;
T$FCTL1:
	CALL	GETUD	; RETURN TO BASE USER/DISK
	LHLD	V$NEXTCH	; GET PTR
	MOV	A,M	; GET DELIM
	CPI	','	; ANOTHER FILE?
	JNZ	T$RETURN
	INX	H	; PT TO CHAR AFTER COMMA
	JMP	T$DSPEC1	; CONTINUE PROCESSING
;
;  INSPECT FILES -- THIS ROUTINE IS TO PERFORM A FILE INSPECTION
;	ON INPUT, HL PTS TO FIRST 16-BYTE ENTRY AND BC=NUMBER OF ENTRIES
;
T$ICHECK:
	MOV	A,B	;ANY FILES?
	ORA	C	;0=NO
	RZ
	PUSH	H	;SAVE PTRS
	PUSH	B
	LXI	D,C$ESIZE	;SIZE OF ENTRY
T$ICHK1:
	MVI	M,0	;CLEAR MSBYTES
	DAD	D	;PT TO NEXT
	DCX	B	;COUNT DOWN
	MOV	A,B	;DONE?
	ORA	C
	JNZ	T$ICHK1
	POP	B	;RESTORE PTRS
	POP	H
	LDA	V$INSPECT	;INSPECT?
	ORA	A	;0=NO
	RZ
	CALL	PRINT
	DB	CR,LF,'File Inspect Mode',0
T$ICHK2:
	CALL	PRINT
	DB	CR,LF,'Select ',0
	CALL	E$PRFN	;PRINT FILE NAME
	CALL	PRINT
	DB	' -- (Y/N/Q=Select Rest/S=Skip Rest/other=Y)? '
	DB	0
	CALL	CIN	;GET RESPONSE
	CALL	CAPS	;CAPITALIZE
	CALL	COUT	;ECHO
	CPI	'Q'	;SELECT REST?
	JZ	T$ICHKYR
	CPI	'S'	;SKIP REST
	JZ	T$ICHKNR
	CPI	'N'	;NO TO THIS ONE?
	JNZ	T$ICHK3
	MVI	M,0FFH	;SET NO FLAG IN FILE FCB
T$ICHK3:
	DAD	D	;PT TO NEXT ONE
	DCX	B	;COUNT DOWN
	MOV	A,B	;DONE?
	ORA	C
	JNZ	T$ICHK2
	RET
;  CHECK REST OF FILES AS SELECTED
T$ICHKYR:
	CALL	PRINT
	DB	CR,LF,'	Rest of Files Selected',0
	RET
;  CHECK REST OF FILES AS NOT SELECTED
T$ICHKNR:
	MVI	M,0FFH	;SET NO FLAG
	DAD	D	;PT TO NEXT
	DCX	B	;COUNT DOWN
	MOV	A,B	;DONE?
	ORA	C
	JNZ	T$ICHKNR
	CALL	PRINT
	DB	CR,LF,'	Rest of Files NOT Selected',0
	RET
;
;  UTILITIES
;	T$SBLANK  -- SKIP BLANKS PTED TO BY HL UNTIL NON-BLANK ENCOUNTERED; HL
;	T$SNBLANK -- SKIP NON-BLANKS PTED TO BY HL UNTIL BLANK OR EOL; HL
;	E$PRFN    -- PRINT FILE NAME PTED TO BY HL; AFFECT NOTHING

;
;  SKIP UNTIL NON-BLANK
;
T$SBLANK:
	MOV	A,M	; LOOK FOR BLANK
	INX	H	; PT TO NEXT
	CPI	' '	; BLANK?
	JZ	T$SBLANK
	DCX	H	; BACK UP
	RET

;
;  SKIP UNTIL BLANK OR EOL
;
T$SNBLANK:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CPI	' '	; BLANK?
	JZ	T$SNB1
	ORA	A	; EOL?
	JNZ	T$SNBLANK
T$SNB1:
	DCX	H	; BACK UP
	RET

;
;  PRINT FILE NAME PTED TO BY HL
;	OUTPUT TO CON:
;
E$PRFN:
	PUSH	H	; SAVE REGS
	PUSH	B
	CALL	RETUD	; GET CURRENT USER/DISK
	MOV	A,B	; GET DISK NUMBER
	ADI	'A'	; CONVERT TO LETTER
	CALL	COUT	; PRINT LETTER
	MOV	A,C	; GET USER NUMBER
	CALL	PADC	; PRINT USER NUMBER
	CALL	PRINT
	DB	': ',0
	INX	H	; PT TO FILE NAME
	MVI	B,8	; PRINT NAME
	CALL	T$PRNT
	MVI	A,'.'	; DECIMAL
	CALL	COUT
	MVI	B,3	; PRINT TYPE
	CALL	T$PRNT
	POP	B	; GET REGS
	POP	H
	RET

;
;  PRINT CHARS PTED TO BY HL FOR B BYTES
;	OUTPUT TO CON:
;
T$PRNT:
	MOV	A,M	; GET CHAR
	CALL	COUT
	INX	H	; PT TO NEXT
	DCR	B	; COUNT DOWN
	JNZ	T$PRNT
	RET

;***********************************************
;*  Application-Specific Section
;***********************************************

;****************************************************
;*
;*  Function-Specific Routines
;*	These routines need to be customized for the
;* specific function being performed.  These, in
;* effect, implement the function.  Other Entry Points
;* useful to the programmer are:
;*		E$FCTLNXT -- Clean Abort of Current
;*				Processing and Skip
;*				to Next File in List;
;*				This is a Clean Way to
;*				Abort FUNCTION for the
;*				Next File
;*		E$PRFN    -- Print File Name Pted to
;*				by HL; No Regs Affected
;*
;****************************************************

;
;  **** EMERGENCY ABORT
;
ABORT:
	CALL	PRINT
	db	cr,lf,'** PHELP Abort **'
	DB	0
	CALL	GETUD	; RETURN HOME
	JMP	T$RETURN
;
;  **** BANNER -- PRINT BANNER FOR PROGRAM (PROGRAM NAME ET AL)
;
BANNER:
	CALL	PRINT
	db	'PHELP  Version '
	db	(vers/10)+'0','.',(vers mod 10)+'0'
	DB	0
	RET
;
;  **** HELP -- PRINT PROGRAM'S HELP MESSAGE
;
HELP:
	CALL	PRINT
	db	cr,lf,'	PHELP is used to print out HLP Files.  It is invoked'
	db	cr,lf,'by a command line of the following form: '
	db	cr,lf,'		PHELP file1,file2,...,filen o...'
	db	cr,lf,'where "filen" is an ambiguous file name and "o" is zero'
	db	cr,lf,'or more of the following options:'
	db	cr,lf,'	H@head@	-- Use "head" as header on every page'
	db	cr,lf,'		   (@ is any character, used as a delimiter)'
	db	cr,lf,'	I	-- Inspect Files for Printing'
	db	cr,lf,'	L	-- Number Each Line'
	db	cr,lf,'	Snn	-- Skip to Page nn and Start Printing'
	db	cr,lf,'	T	-- Toggle Time Display'
	db	cr,lf
	db	cr,lf,'	PHELP assumes HLP to be the file type if none is given'
	DB	0
	RET
;
;  **** PROGRAM INIT ROUTINE
;	THIS ROUTINE IS USED BY THE PROGRAM TO PERFORM ANY NECESSARY
;	INITIALIZATIONS
;
INIT:
	lxi	h,dlnumfl	;copy defaults into buffers
	lxi	d,lnumfl
	mvi	b,3	;3 bytes
	call	moveb
	xra	a	;A=0
	sta	skipfl	;set no skip
	sta	hbuf	;set no heading
;
	if	timeok
	call	time	;get time string
	lxi	d,timebf	;store in buffer
initt:
	mov	a,m	;get byte
	stax	d
	inx	h	;pt to next
	inx	d
	ora	a	;done?
	jnz	initt
	endif
;
	RET
;
;  **** FUNCTION COMPLETE -- CLEANUP AND EXIT
;	FILL THIS IN WITH CLEANUP CODE FOR EXIT
;
DINIT:
	RET
;
;  **** OPTION TABLE USED TO PROCESS COMMAND LINE
;	EACH OPTION IS A CAPITAL LETTER OR SPECIAL CHAR FOLLOWED BY
;		AN ADDRESS; THE TABLE IS TERMINATED BY A BINARY ZERO
;
OPTAB:
	DB	'I'	; FILE INSPECTION OPTION
	DW	OPTINSP	; REMOVE THESE TWO LINES AND THE FOLLOWING ROUTINE
			;   IF YOU DO NOT WANT FILE INSPECTION OPTION IN
			;   COMMAND LINE
	db	'H'	;heading
	dw	opthead
	db	'L'	;line numbers
	dw	optln
	db	'S'	;skip
	dw	optskip
;
	if	timeok
	db	'T'	;time display
	dw	opttime
	endif
;
	DB	0	; END OF TABLE
;
;  **** OPTION ROUTINES
;	EACH ROUTINE IS PROVIDED THE OPTION CHARACTER IN THE A REGISTER
;	AND A POINTER TO THE NEXT CHARACTER IN THE COMMAND LINE IN THE
;	HL REGISTER PAIR; ONLY HL NEED TO BE PRESERVED (WITH OPTIONAL
;	ADVANCEMENT TO THE NEXT OPTION) ON EXIT
;
OPTINSP:
	MVI	A,0FFH	; TURN ON FILE INSPECTION OPTION
	STA	V$INSPECT	; THIS IS PROVIDED AS A SAMPLE ROUTINE
				;   AND FOR THE INDICATED FUNCTION
	RET
;
;  Option:  H (Set Heading)
;
opthead:
	lxi	d,hbuf	;pt to heading buffer
	mov	a,m	;get delim
	ora	a	;none?
	rz
	mov	b,a	;delim in B
	inx	h	;pt to next char
opthd1:
	mov	a,m	;get next char
	ora	a	;done?
	jz	opthd3
	cmp	b	;done?
	jz	opthd2
	stax	d	;save char
	inx	h	;pt to next
	inx	d
	jmp	opthd1
opthd2:
	inx	h	;skip over delim
opthd3:
	xra	a	;store ending 0
	stax	d
	ret
;
;  Option:  L (Set Line Numbering)
;
optln:
	lda	lnumfl	;flip flag
	cma
	sta	lnumfl
	ret
;
;  Option:  S (Skip Lines)
;
optskip:
	mvi	a,0ffh	;set flag
	sta	skipfl
	call	eval10	;get number
	xchg
	shld	skipnum	;set page number to skip to
	xchg		;HL pts to next char
	mov	a,d	;see if page number was zero
	ora	e
	rnz
	xra	a	;if zero, turn off skip flag
	sta	skipfl
	ret
;
	if	timeok
;
;  Set Time Flag
;
opttime:
	lda	timepfl	;flip flag
	cma
	sta	timepfl
	ret
;
	endif
;
;  **** FUNCTION -- MAIN FUNCTION OF TEMPLATE
;	ON ENTRY, HL PTS TO NAME OF FILE (16 BYTES) AND USER IS LOGGED INTO
;		DIRECTORY CONTAINING INDICATED FILE
;
FUNCTION:
;
;  HELP FILE PRINT Routine -- Print the Help File Whose Name is Pointed to by
;	HL; we are already logged into the correct directory
;
	call	prinit	;init print buffers
	call	fload	;load buffer initially
	call	prhead	;print heading line
	lhld	V$SCRATCH	;pt to first char in file
	shld	nxtln	;set pointer to next line
	mvi	a,0ffh	;first line
	sta	firstf
	call	prline	;print first line (special case)
	xra	a	;not first line now
	sta	firstf
fprloop:
	call	prline	;print line of file
	jnz	fprloop	;done if EOF
	call	page	;advance to top of next page
	ret
;
;  Init Print Buffers and Print File Name
;
prinit:
	lxi	d,tfcb	;set up FCB
	mvi	b,12	;12 bytes
	call	moveb
	lxi	h,0	;HL=0
	shld	lnum	;set line number
	inx	h	;HL=1
	shld	pnum	;set page number
	lda	ltpp	;set line count
	sta	lcount
	call	print
	db	cr,lf,'Printing Help File ',0
	lxi	h,tfcb	;pt to FCB
	call	e$prfn	;print file name
	ret
;
;  FILE LOAD (FLOAD) Routine -- Initial Load of memory buffer
;
fload:
	lxi	d,tfcb	;pt to file fcb
	call	initfcb	;init file's fcb
	call	f$open	;open file for input
	jz	fload1	;open was OK
	call	print
	db	cr,lf,'File ',0
	xchg		;HL pts to FCB
	call	e$prfn	;print file name
	call	print
	db	' NOT Found',0
	pop	d	;clear return address
	ret		;abort printout of this file
;
;  This is an entry point for further memory loads of the file
;
fload1:
	lda	V$BCNT	;get number of blocks to load
	mov	c,a	;... in C
	lhld	V$SCRATCH	;get address of first block to load into
	shld	nxtblk	;set pointer to next block to load
fload2:
	call	rdblk	;read a block (128 bytes)
	jnz	eof	;eof encountered?
	call	rdblk	;read another block (128 bytes)
	jnz	eof	;eof encountered?
	dcr	c	;count down
	jnz	fload2
	lhld	nxtblk	;pt to next byte to load
	mvi	m,eold	;mark end of load
	ret
eof:
	lxi	d,tfcb	;close file
	call	f$close
	lhld	nxtblk	;ensure ^Z
	mvi	m,ctrlz
	ret
rdblk:
	lxi	d,tfcb	;pt to FCB
	call	f$read	;read next block
	ora	a	;error?
	rnz
	lhld	nxtblk	;get ptr to next block
	xchg		; as dest
	lxi	h,tbuff	;ptr to DMA address
	mvi	b,128	;copy 128 bytes
rdblk1:
	mov	a,m	;get byte
	ani	7fh	;mask out msb
	stax	d	;put byte
	inx	h	;pt to next
	inx	d
	dcr	b	;count down
	jnz	rdblk1
	xchg		;new nxtblock
	shld	nxtblk
	ret

;
;  Line Print Routine
;	Print Next Line with Optional Disk Load
;	Input Parameter is NXTLN, which is the address of the first char
; on the next line
;	Output Parameter is Zero Flag, with Z meaning done with print, NZ
; meaning more yet to print
;
prline:
	lhld	lnum	;increment line number
	inx	h
	shld	lnum
	lhld	nxtln	;pt to first char of next line
	mvi	c,0	;init char count
	mov	a,m	;get first char of line
	cpi	ctrlz	;EOF?
	cnz	prlnum	;print line number (optional)
	lda	firstf	;first char?
	ora	a	;0=no
	jnz	prl1
	mov	a,m	;get first char of line
	cpi	':'	;new information section?
	jz	prff	;set it as such
prl1:
	mov	a,m	;get char
	cpi	eold	;end of load?
	jz	prload
	cpi	ctrlz	;eof?
	jz	prexit
	inx	h	;pt to next char
	cpi	ctrli	;tab?
	jz	prtab
	cpi	cr	;<CR>?
	jz	prldn
	cpi	ff	;form feed?
	jz	prldn
	cpi	lf	;end of line?
	jz	prl1
	cpi	ctrlh	;back space?
	jz	prbs
	cpi	ctrlg	;ring bell?
	jz	prbell
	cpi	del	;delete char?
	jz	prl1	;skip it
	cpi	' '	;other control char?
	jc	prl1	;skip if other control char
	call	prout	;print char
	inr	c	;increment char count
	call	eoltest	;check to see if at end of line and newline if so
	jmp	prl1
;
;  End of Load Reached -- Load More of File from Disk
;
prload:
	push	b	;save char count
	call	fload1	;use load routine
	pop	b	;get char count
	lhld	V$SCRATCH	;next byte is here
	jmp	prl1	;continue processing
;
;  Tabulate
;
prtab:
	mvi	a,' '	;space
	call	prout
	inr	c	;new char
	call	eoltest	;process EOL
	mov	a,c	;done?
	ani	7
	jnz	prtab	;continue tabulation
	jmp	prl1	;continue processing
;
;  Exit with Zero Flag Set if Done
;
prexit:
	xra	a	;set zero flag
	ret
;
;  Form Feed -- Advance to Top of Next Page
;
prff:
	call	page	;page eject with heading
	mvi	c,0	;reset char count
	jmp	prl1	;continue processing
;
;  Carriage Return -- End of Routine
;
prldn:
	mov	a,m	;skip to non-LF
	cpi	lf
	jnz	prldn1
	inx	h	;skip to first char of next line
prldn1:
	mvi	a,cr	;output <CR>
	call	prout
	mvi	a,lf	;output <LF>
	call	prout	;echo LF to printer
	shld	nxtln	;set ptr to first char of next line
	mvi	a,0ffh	;set not done
	ora	a	;set flags
	ret
;
;  Backspace on Printer
;
prbs:
	mov	a,c	;check for beginning of line
	ora	a
	jz	prl1	;continue if at BOL
	mvi	a,ctrlh	;backspace
	call	prout
	dcr	c	;back up char position
	jmp	prl1	;continue
;
;  Ring Bell on Printer
;
prbell:
	call	prout	;ring the bell
	jmp	prl1	;continue without advancing char position
;
;  Test for End of Line and Process if so
;
eoltest:
	lda	lwidth	;get line width
	sui	4	;4 chars less for continuation mark
	mov	b,a	;result in B
	lda	lnumfl	;line numbering (lines are 7 chars shorter if so)
	ora	a	;0=no
	jz	eolt1
	mov	a,b	;reduce by 7 for line numbers
	sui	7
	mov	b,a
eolt1:
	mov	a,b	;get line width
	cmp	c	;there?
	rnz		;continue if not
	mov	a,m	;get next char
	cpi	cr	;new line next?
	rz		;continue if so
	cpi	ctrlh	;backspace next?
	rz		;continue if so
	mvi	a,' '	;print continuation chars
	call	prout
	mvi	a,'<'
	call	prout
	mvi	a,'<'
	call	prout
	mvi	a,cr	;new line
	call	prout
	mvi	a,lf
	call	prout
	mvi	c,0	;reset char position
	lda	skipfl	;skipping?
	ora	a	;0=no
	rnz
	lda	lnumfl	;printing line numbers?
	ora	a	;0=no
	rz
	call	lprint
	db	'     : ',0
	ret
;
;  Output a character to the printer
;	A = Character
;
prout:
	mov	b,a	;char in B
	call	condin	;check for abort
	jz	prout1
	cpi	ctrlc	;abort?
	jz	abort
	cpi	ctrlx	;abort this one file?
	jz	cxabort
prout1:
	lda	skipfl	;skipping?
	ora	a	;set flags (Z=no skip=print char)
	mov	a,b	;restore char
	cz	lout	;send character to printer
	cpi	lf	;special tests if it is a line feed
	rnz		;done if non-LF char
	lda	lcount	;decrement line counter
	dcr	a
	sta	lcount
	rnz
;
;  Paging Required
;	Skip to top of next page; reset LCOUNT (Lines Left on Page Count);
;	print header
;
prout0:
	lda	ltpp	;get number of text lines per page
	sta	lcount	;set as new line count
	push	h	;save ptr
	lhld	pnum	;increment page number
	inx	h
	shld	pnum
	lda	lspp	;get number of lines to skip per page
	call	lineskp	;skip lines
	pop	h	;restore ptr
	mov	a,m	;check next character
	cpi	ctrlz	;EOF?
	cnz	prhead	;print 2-line heading if NOT EOF
	ret
;
;  Abort current file with final page eject
;
cxabort:
	lda	lcount	;get count of remaining lines
	call	lineskp	;skip lines
	lda	lspp	;number of lines to skip per page
	call	lineskp	;skip lines
	jmp	e$fctlnxt	;continue with next file
;
;  Skip out rest of page
;	Form Feed Function
;
page:
	lda	lcount	;get count of remaining lines
	call	lineskp	;skip lines
	jmp	prout0	;process top of new page
;
;  Skip out lines on page
;	A = number of lines to skip
;
lineskp:
	mov	b,a	;line count in B
	ora	a	;any?
	rz
	lda	skipfl	;skipping?
	ora	a
	rnz
lines1:
	mvi	a,cr	;output new line to printer
	call	lout
	mvi	a,lf
	call	lout
	dcr	b	;count down
	jnz	lines1
	ret
;
;  Print Line Number (optional)
;
prlnum:
	lda	skipfl	;skipping?
	ora	a	;0=no
	rnz
	lda	lnumfl	;get flag
	ora	a	;0=don't number lines
	rz
	push	h	;save ptr
	lhld	lnum	;get line number
	call	lhldc	;print line number
	call	lprint	;print separator
	db	': ',0
	pop	h	;restore ptr
	ret
;
;  Print 2-line heading and control skipping
;
prhead:
	push	h	;save ptr
	lda	skipfl	;currently skipping?
	ora	a	;0=no
	cnz	skiptst	;test for shut off
	call	prpnum	;print page heading and number
	call	e$prfname	;print file name
;
	if	timeok	;time available?
	lda	timepfl	;print time?
	ora	a	;0=no
	cnz	prtime	;print time
	endif
;
	lda	hbuf	;print heading?
	ora	a	;0=no
	cnz	prhdg	;print heading
	pop	h	;restore ptr
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	lcrlf	;new line
	jmp	lcrlf
;
;  Test for completion of skipping
;
skiptst:
	lhld	pnum	;get page number
	xchg		;... in DE
	lhld	skipnum	;get page to skip to
	mov	a,h	;compare them
	cmp	d
	rnz
	mov	a,l
	cmp	e
	rnz
	xra	a	;A=0 to stop skipping
	sta	skipfl	;set flag
	ret
;
;  Print Page Number
;
prpnum:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	lprint	;print header
	db	'Page ',0
	lhld	pnum	;print current page number
	call	lhldc	;print as decimal
	ret
;
;  Print File Name
;
e$prfname:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	lprint	;print separator
	db	' -- Help File: ',0
	lxi	h,tfcb+1	;pt to first char
	mvi	b,8	;8 chars
	call	lfn1
	mvi	a,'.'
	call	lout
	mvi	b,3	;3 chars
	call	lfn1
	ret
lfn1:
	mov	a,m	;get char
	ani	7fh	;mask
	call	lout	;send to printer
	inx	h	;pt to next
	dcr	b	;count down
	jnz	lfn1
	ret
;
;  Print Separator
;
prdash:
	call	lprint
	db	' -- ',0
	ret
;
	if	timeok
;
;  Print Time
;
prtime:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,timebf	;pt to time stamp
	call	lpstr	;print
	ret
;
	endif
;
;  Print Header
;
prhdg:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,hbuf	;pt to heading
	call	lpstr	;print
	ret
	RET
;
;  **** BUFFERS -- PLACE APPLICATION-SPECIFIC BUFFERS HERE
;
firstf:
	ds	1	;first line in file flag
tfcb:
	ds	36	;FCB for current file
nxtblk:
	ds	2	;ptr to next block to load
nxtln:
	ds	2	;ptr to next line to read
lcount:
	ds	1	;count of text lines left on page
hlptyp:
	db	'HLP'	;file type of HLP file

;***********************************************
;*  End of Application-Specific Section
;***********************************************

;
;  BUFFERS
;
V$DISK:
	DS	1	; HOME DISK NUMBER
V$USER:
	DS	1	; HOME USER NUMBER
V$CDISK:
	DS	1	; CURRENT DISK NUMBER
V$CUSER:
	DS	1	; CURRENT USER NUMBER
V$CMDLNE:
	DS	2	; PTR TO COMMAND LINE STRING
V$NEXTCH:
	DS	2	; PTR TO NEXT CHAR IN MULTIFILE COMMAND LINE
V$FILECNT:
	DS	2	; COUNT OF NUMBER OF FILES RENAMED
V$SCRATCH:
	DS	2	; ADDRESS OF FIRST BYTE OF SCRATCH AREA
V$BCNT:
	DS	1	; NUMBER OF PAGES IN SCRATCH AREA
V$INSPECT:
	DS	1	; INSPECT FLAG
V$NTFCB:
	DS	36	; FCB FOR NEW FILE
;
;  Stack
;
	DS	100	; STACK AREA
V$STACK:
	DS	2	; OLD STACK PTR

	END
